package batch.status;

import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.apache.logging.log4j.LogManager;

import common.db.jdbc.Jdbc;

import core.config.Env;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;
import core.util.bean.Tuple;

/**
 * ジョブファイル管理テーブル取得／更新クラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class JobFileStatusImpl implements JobFileStatus {

	/** テーブル名 ファイル管理 */
	private static final String TABLE = Env.getEnv("JobFile.Table");

	/** カラム名 ジョブ連番 */
	private static final String COL_JOB_SEQ = Env.getEnv("JobFile.JobSeq");
	/** カラム名 バッチ連番 */
	private static final String COL_BAT_SEQ = Env.getEnv("JobFile.BatSeq");
	/** カラム名 ファイル連番 */
	private static final String COL_FILE_SEQ = Env.getEnv("JobFile.FileSeq");
	/** カラム名 ファイル名 */
	private static final String COL_FILE_MEI = Env.getEnv("JobFile.FileMei");
	/** カラム名 パス名 */
	private static final String COL_PATH_MEI = Env.getEnv("JobFile.PathMei");
	/** カラム名 ファイルサイズ */
	private static final String COL_FILE_SIZE = Env.getEnv("JobFile.FileSize");
	/** カラム名 ダウンロード名 */
	private static final String COL_DOWNLOAD_MEI = Env.getEnv("JobFile.DownloadMei");
	/** カラム名 ダウンロード回数 */
	private static final String COL_DOWNLOAD_COUNT = Env.getEnv("JobFile.DownloadCount");

	/**
	 * テーブル名取得
	 *
	 * @return テーブル名
	 */
	@Override
	public String getTableName() {
		return TABLE;
	}

	/**
	 * ファイル管理レコード取得
	 *
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @param conn コネクション
	 * @param query クエリ
	 * @return レコード情報
	 */
	private JobFile getJobFile(final long jseq, final int dseq,
			final int fseq, final Connection conn, final String query) {
		try (PreparedStatement psmt = Jdbc.wrap(conn).readonlyStatement(query)) {
			// ジョブ管理テーブル読み込み
			int i = 1;
			psmt.setLong(i++, jseq);
			psmt.setInt(i++, dseq);
			psmt.setInt(i++, fseq);

			JobFile ret = null;
			try (ResultSet rs = psmt.executeQuery()) {
				if (rs.next()) {
					ret = toJobFileObject(rs);
				}
			}
			return ret;

		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ファイル管理レコードリスト取得
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return レコードリスト
	 */
	@Override
	public List<JobFile> selectJobFiles(final Connection conn, final long seq) {
		final String query = String.format(
				"SELECT * FROM %s WHERE %s = ? ORDER BY %s DESC, %s DESC ",
				TABLE, COL_JOB_SEQ, COL_BAT_SEQ, COL_FILE_SEQ);

		try (PreparedStatement psmt = Jdbc.wrap(conn).readonlyStatement(query)) {
			psmt.setLong(1, seq);

			List<JobFile> ret = new ArrayList<>();
			try (ResultSet rs = psmt.executeQuery()) {
				while (rs.next()) {
					ret.add(toJobFileObject(rs));
				}
			}
			return ret;

		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ファイル管理レコード更新
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @param count ダウンロード回数
	 * @param size ファイルサイズ
	 * @return 更新件数
	 */
	@Override
	public int updateJobFile(final Connection conn, final long jseq, final int dseq,
			final int fseq, final int count, final long size) {
		final String sql = String.format(
				"UPDATE %s SET %s = ? , %s = ? WHERE %s = ? AND %s = ? AND %s = ? ",
				TABLE, COL_DOWNLOAD_COUNT, COL_FILE_SIZE, COL_JOB_SEQ, COL_BAT_SEQ, COL_FILE_SEQ);

		try (PreparedStatement psmt = Jdbc.wrap(conn).prepareStatement(sql)) {
			int i = 1;
			psmt.setInt(i++, count);
			psmt.setLong(i++, size);
			psmt.setLong(i++, jseq);
			psmt.setInt(i++, dseq);
			psmt.setInt(i++, fseq);
			return psmt.executeUpdate();
		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ファイル管理削除
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return 削除件数
	 */
	@Override
	public int deleteJobFile(final Connection conn, final long seq) {
		final String sql = String.format("DELETE FROM %s WHERE %s = ? ", TABLE, COL_JOB_SEQ);
		try (PreparedStatement psmt = Jdbc.wrap(conn).prepareStatement(sql)) {
			// ファイル管理削除
			psmt.setLong(1, seq);
			return  psmt.executeUpdate();
		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ファイル管理レコード新規作成
	 *
	 * @param conn コネクション
	 * @param jfile ジョブファイル
	 * @return 作成件数
	 */
	@Override
	public int insertJobFile(final Connection conn, final JobFile jfile) {
		final String sql = String.format(
				"INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s) VALUES(?, ?, ?, ?, ?, ?, ?, ?)",
				TABLE, COL_JOB_SEQ, COL_BAT_SEQ, COL_FILE_SEQ, COL_FILE_MEI,
				COL_PATH_MEI, COL_FILE_SIZE, COL_DOWNLOAD_MEI, COL_DOWNLOAD_COUNT);

		try (PreparedStatement psmt = Jdbc.wrap(conn).prepareStatement(sql)) {
			// ファイル管理テーブル更新
			int i = 1;
			psmt.setLong(i++, jfile.getJobSeq());
			psmt.setInt(i++, jfile.getBatSeq());
			psmt.setInt(i++, jfile.getFileSeq());
			psmt.setString(i++, jfile.getFileName());
			psmt.setString(i++, jfile.getPathName());
			psmt.setLong(i++, jfile.getFileSize());
			psmt.setString(i++, jfile.getDownloadName());
			psmt.setInt(i++, 0);
			return psmt.executeUpdate();
		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * バッチ生成ファイル追加
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param dtlno バッチ連番
	 * @param list 作成ファイルリスト
	 * @return 作成レコード数
	 */
	@Override
	public int setFiles(final Connection conn, final long seq,
			final int dtlno, final List<Tuple<String>> list) {

		int ret = 0;
		if (list != null) {
			int fseq = 1;
			for (final Tuple<String> strs : list) {
				// フルパスファイル名取得
				String fullpath = strs.get(0);
				if (fullpath == null) {
					continue;
				}

				File f = new File(fullpath);

				int cnt = 0;
				// ファイル管理テーブル更新
				JobFile info = getJobFileWithLock(conn, seq, dtlno, fseq);
				if (info == null) {
					info = new JobFile();
					info.setJobSeq(seq);
					info.setBatSeq(dtlno);
					info.setFileSeq(fseq);
					info.setFileName(f.getName());
					info.setPathName(new File(f.getParent()).toURI().getPath());
					info.setFileSize(f.length());
					info.setDownloadName(strs.get(1));
					cnt = insertJobFile(conn, info);
				} else {
					cnt = updateJobFile(conn, seq, dtlno, fseq,
								info.getDownloadCount(), f.length());
				}

				if (cnt != 1) {
					throw new IllegalStateException(String.valueOf(cnt));
				}

				fseq++;
				ret += cnt;
			}
		}
		return ret;
	}

	/**
	 * ファイル削除
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return 処理結果数
	 */
	@Override
	public int deleteFiles(final Connection conn, final long seq) {
		int rmfile = 0;
		List<JobFile> l = selectJobFiles(conn, seq);
		if (!l.isEmpty()) {
			for (final JobFile jf : l) {
				if (removeFile(jf.getFileName(), jf.getPathName())) {
					rmfile++;
				}
			}
		}
		return rmfile;
	}

	/**
	 * ファイル削除処理
	 *
	 * @param fname ファイル名
	 * @param path パス名
	 * @return 正常に削除された場合 true
	 */
	private boolean removeFile(final String fname, final String path) {
		if (Objects.toString(fname, "").trim().isEmpty()) {
			return false;
		}

		String p = Objects.toString(path, "");
		// 作成ファイルの削除
		LogManager.getLogger(JobFileStatusImpl.class).warn("remove path={} file={}", p, fname);
		File f = new File(p, fname);
		return f.delete();
	}

	/**
	 * ファイル管理テーブルのダウンロード回数を更新
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @return 取得項目値
	 */
	@Override
	public JobFile countUpDownload(final Connection conn, final long jseq,
			final int dseq, final int fseq) {
		// ファイル管理情報取得
		JobFile ret = getJobFileWithLock(conn, jseq, dseq, fseq);
		if (ret != null) {
			int count = ret.getDownloadCount();
			long size = ret.getFileSize();
			int cnt = updateJobFile(conn, jseq, dseq, fseq, count + 1, size);
			if (cnt != 1) {
				throw new IllegalStateException(String.valueOf(cnt));
			}
		}
		return ret;
	}

	/**
	 * ファイル管理レコード取得
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @return レコード情報
	 */
	@Override
	public JobFile getJobFileWithLock(final Connection conn, final long jseq,
			final int dseq, final int fseq) {
		final String query = String.format(
				"SELECT * FROM %s WHERE %s = ? AND %s = ? AND %s = ? FOR UPDATE",
				TABLE, COL_JOB_SEQ, COL_BAT_SEQ, COL_FILE_SEQ);
		return getJobFile(jseq, dseq, fseq, conn, query);
	}

	/**
	 * ファイル管理レコード取得
	 *
	 * @param conn コネクション
	 * @param jseq ジョブ連番
	 * @param dseq ジョブ詳細連番
	 * @param fseq ファイル連番
	 * @return レコード情報
	 */
	@Override
	public JobFile getJobFile(final Connection conn, final long jseq,
			final int dseq, final int fseq) {
		final String query = String.format(
				"SELECT * FROM %s WHERE %s = ? AND %s = ? AND %s = ? ",
				TABLE, COL_JOB_SEQ, COL_BAT_SEQ, COL_FILE_SEQ);
		return getJobFile(jseq, dseq, fseq, conn, query);
	}

	/**
	 * ジョブファイル管理オブジェクト取得
	 *
	 * @param rs 結果セット
	 * @return ジョブファイル管理
	 * @throws SQLException SQL例外
	 */
	private JobFile toJobFileObject(final ResultSet rs) throws SQLException {
		JobFile ret = new JobFile();
		ret.setJobSeq(rs.getLong(COL_JOB_SEQ));
		ret.setBatSeq(rs.getInt(COL_BAT_SEQ));
		ret.setFileSeq(rs.getInt(COL_FILE_SEQ));
		ret.setFileName(rs.getString(COL_FILE_MEI));
		ret.setPathName(rs.getString(COL_PATH_MEI));
		ret.setDownloadName(rs.getString(COL_DOWNLOAD_MEI));
		ret.setDownloadCount(rs.getInt(COL_DOWNLOAD_COUNT));
		ret.setFileSize(rs.getLong(COL_FILE_SIZE));
		return ret;
	}
}
